Skip to content

fix(sessions): paused-turn transcript hygiene + pause-sentinel replay nudges#5451

Open
ardaerzin wants to merge 3 commits into
feat/sessions-storage-reworkfrom
fix/transcript-hygiene
Open

fix(sessions): paused-turn transcript hygiene + pause-sentinel replay nudges#5451
ardaerzin wants to merge 3 commits into
feat/sessions-storage-reworkfrom
fix/transcript-hygiene

Conversation

@ardaerzin

@ardaerzin ardaerzin commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Stacks on feat/sessions-storage-rework (PR #5436). Two of the approval fixes from the handoff, rebuilt against the merged code.

1. Paused-turn transcript hygiene (handoff task 2)

A parked-then-resumed approval turn rehydrated wrong on a cold reload:

  • Duplicate user row on resume. The runner re-persisted the prompt on every resume; an approval reply's tail is the tool_result envelope (no text), so resolvePromptText fell back to the original prompt and wrote a second user row. Guarded with tailIsFreshUserMessage.
  • No marker for a turn that ended mid-approval. finish() now stamps stopReason:"paused" on the terminal done record (existing wire field — no contract change); transcriptToMessages carries it to message.metadata.paused.
  • With the duplicate row gone, AgentConversation's adoption heuristic can no longer lean on a bumped message count — it's now state-aware (adopt when the server is ahead by count OR the local tail is stuck paused while the server turn is terminal).

2. Pause-sentinel replay nudges (handoff task 3)

On cold replay messageTranscript fed both pause sentinels to the model through the generic [<tool> error: …] branch. "error" makes the model read a parked call as a refusal and abandon it, and for an approved-but-unobserved call it invites a retry of something that may already have run. Each now renders as an explicit, non-error nudge with opposite guidance:

  • DEFERRED_NOT_EXECUTED → skipped for another approval, not denied; re-issue the call.
  • APPROVED_EXECUTION_RESULT_UNKNOWN → may already have run; do not assume failure, do not retry a side-effecting call.

The user-facing render already distinguished both (ToolActivity); this restores the model-facing half dropped when the fix train folded the deferred check into isPauseSyntheticResult.

Verification

  • Runner tsc --noEmit clean; 1246/1246 unit tests pass (incl. the wire-contract golden)
  • FE pnpm --filter @agenta/oss exec tsc --noEmit — zero errors in the changed files
  • transcriptToMessages.test.ts 8/8, transcript.test.ts 22/22

Two coupled defects made a parked-then-resumed approval turn rehydrate wrong
on a cold reload:

- The runner re-persisted the user prompt on every resume. An approval reply's
  tail is the tool_result envelope (no text), so resolvePromptText fell back to
  the ORIGINAL prompt and wrote a duplicate user row. Guard the write with
  tailIsFreshUserMessage so the prompt persists only on the turn that introduced
  it.
- Nothing marked a turn that ended mid-approval. finish() now stamps
  stopReason:"paused" on the terminal `done` record (existing wire field, no
  contract change); transcriptToMessages carries it to message.metadata.paused.

With the duplicate row gone, AgentConversation's server-transcript adoption can
no longer lean on a bumped message count. Make it state-aware: adopt when the
server is ahead by count OR when the local tail is stuck paused while the server
turn is terminal (a resume that completed elsewhere).

Adds paused-end-marker coverage to transcriptToMessages.test.ts.
@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Ready Ready Preview, Comment Jul 22, 2026 8:30pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 82cc041e-708b-416c-9d76-4683596f32a5

📥 Commits

Reviewing files that changed from the base of the PR and between 02d1aeb and 7787459.

📒 Files selected for processing (1)
  • web/oss/src/components/AgentChatSlice/AgentConversation.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/oss/src/components/AgentChatSlice/AgentConversation.tsx

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of paused turns and approval-related transcript replay.
    • Prevented duplicate user messages when resuming approval flows.
    • Improved conversation synchronization after paused or completed turns.
    • Clarified transcript guidance for deferred or approved-but-unobserved tool actions.
  • Enhancements
    • Paused turn status is now preserved during transcript replay and refreshes.
    • Completion records distinguish paused runs from normally completed runs.

Walkthrough

Paused stop reasons now flow through runner finalization and transcript replay. Approval sentinel outputs receive explicit transcript guidance, resume tails avoid duplicate user persistence, and the web client marks paused messages and reconciles them with completed server transcripts.

Changes

Paused turn flow

Layer / File(s) Summary
Propagate paused terminal state
services/runner/src/tracing/otel.ts, services/runner/src/engines/sandbox_agent/run-turn.ts
Runner finalization accepts stopReason and emits stopReason: "paused" on terminal done records.
Handle approval resume transcripts
services/runner/src/engines/sandbox_agent/transcript.ts, services/runner/src/server.ts, services/runner/tests/unit/transcript.test.ts
Pause sentinels render as retry or do-not-retry guidance, and inbound user messages are persisted only for fresh user tails; tests cover both sentinel cases.
Replay and reconcile paused turns
web/oss/src/components/AgentChatSlice/assets/transcriptToMessages.ts, web/oss/src/components/AgentChatSlice/assets/transcriptToMessages.test.ts, web/oss/src/components/AgentChatSlice/AgentConversation.tsx
Paused terminal records populate message metadata, with tests for paused and normal completion; revalidation adopts a completed server tail over a locally paused approval tail.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SandboxAgent
  participant SandboxAgentOtel
  participant RunnerTranscript
  participant WebReplay
  participant AgentConversation
  SandboxAgent->>SandboxAgentOtel: finish(stopReason)
  SandboxAgentOtel->>WebReplay: emit done with stopReason paused
  SandboxAgent->>RunnerTranscript: render approval tool results
  RunnerTranscript-->>WebReplay: emit retry or do-not-retry guidance
  WebReplay->>AgentConversation: set paused metadata
  AgentConversation->>AgentConversation: reconcile server and local tails
Loading

Possibly related PRs

  • Agenta-AI/agenta#5156: Adds fresh-user-tail and paused-boundary handling connected to the session persistence change.
  • Agenta-AI/agenta#5158: Uses paused turn boundaries in the approval park/resume flow.
  • Agenta-AI/agenta#5267: Drains queued approval or cancellation events before paused sibling tool calls are finalized.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: paused-turn transcript fixes and pause-sentinel replay nudges.
Description check ✅ Passed The description is directly related to the transcript hygiene and pause-sentinel replay changes in the pull request.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 60.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/transcript-hygiene

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. Bug Report Something isn't working Backend Frontend labels Jul 22, 2026
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Railway Preview Environment

Preview URL https://gateway-production-6050.up.railway.app/w
Image tag pr-5451-7706cb4
Status Failed
Railway logs Open logs
Logs View workflow run
Updated at 2026-07-22T20:50:07.669Z

On cold replay `messageTranscript` fed both pause-terminalization sentinels to the
model through the generic `[<tool> error: …]` branch. The "error" framing makes the
model read a parked call as a refusal and abandon it (the parallel-approval bug), and
for an approved-but-unobserved call it invites a retry of something that may already
have run.

Render each sentinel as an explicit, non-error nudge with opposite guidance:
- DEFERRED_NOT_EXECUTED — skipped for another approval, not denied; re-issue the call.
- APPROVED_EXECUTION_RESULT_UNKNOWN — may already have run; do not assume failure, do
  not retry a side-effecting call.

The user-facing render already distinguishes both (ToolActivity); this restores the
model-facing half dropped when the fix train folded the deferred check into the
combined isPauseSyntheticResult.
@ardaerzin ardaerzin changed the title fix(runner): keep a paused turn's persisted transcript hydration-clean fix(sessions): paused-turn transcript hygiene + pause-sentinel replay nudges Jul 22, 2026
@ardaerzin

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6b3871f6-d134-4462-a223-3be98c4b6f10

📥 Commits

Reviewing files that changed from the base of the PR and between 314a420 and 02d1aeb.

📒 Files selected for processing (8)
  • services/runner/src/engines/sandbox_agent/run-turn.ts
  • services/runner/src/engines/sandbox_agent/transcript.ts
  • services/runner/src/server.ts
  • services/runner/src/tracing/otel.ts
  • services/runner/tests/unit/transcript.test.ts
  • web/oss/src/components/AgentChatSlice/AgentConversation.tsx
  • web/oss/src/components/AgentChatSlice/assets/transcriptToMessages.test.ts
  • web/oss/src/components/AgentChatSlice/assets/transcriptToMessages.ts

Comment thread web/oss/src/components/AgentChatSlice/AgentConversation.tsx
…local tail

The paused-tail adoption exception could fire when the server transcript was
shorter than the local one: a lagging snapshot ending in a settled assistant
message would discard newer locally persisted approval state. The intended case
(a resume that completed elsewhere) always has the server at least as long, so
require serverMsgs.length >= prev.length and an assistant tail for the exception.
@ardaerzin

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backend Bug Report Something isn't working Frontend size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant